﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Diagnostics;
using ScrumTable.Common.Logging;
using ScrumTable.Common.Properties;

#endregion 

namespace ScrumTable.Common
{

    /// <summary>
    /// Defines a method to release allocated resources.
    /// </summary>
    public abstract class DisposeBase : IDisposableObject
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private volatile bool _disposed = false;
        private volatile bool _disposing = false;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Returns true, if the current context has been disposed.
        /// </summary>
        bool IDisposableObject.IsDisposed { get { return _disposed; } }

        /// <summary>
        /// Returns true, if the current context is beeing disposed or it
        /// is disposed.
        /// </summary>
        bool IDisposableObject.IsDisposing { get { return _disposing; } }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new DisposeBase instance, no public constructor
        /// needed because it's abstract.
        /// </summary>
        protected DisposeBase()
        {
        }

        /// <summary>
        /// Dispose instance manually.
        /// </summary>
        ~DisposeBase()
        {
            Dispose(false);
            Logger<DisposeBase>.DebugFormat(Resources.LogDispBaseInstanceDisposedByGc, GetType().FullName);
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Implement dispose interface in order to support runtime support.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);

            // Use SupressFinalize in case a subclass
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the managed and unmanaged resources.
        /// </summary>
        /// <param name="disposing">True to dispose the managed resources, otherwise false.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                IList<Exception> disposeErrors = new List<Exception>(2);

                _disposing = true;
                TryFireDisposingChanged(disposeErrors);
                TryReleaseUnmanagedResources(disposeErrors);
                
                if (disposing)
                {
                    TryReleaseManagedResources(disposeErrors);
                }

                _disposed = true; // indicate that the instance has been disposed
                TryFireDisposedChanged(disposeErrors);
                LogInstanceDisposed();
                HandleDisposeErrors(disposeErrors);
            }
        }

        /// <summary>
        /// Releases unmanaged resources.
        /// </summary>
        protected virtual void ReleaseUnmanagedResources()
        {
        }

        /// <summary>
        /// Releases managed (c# or .net) resources.
        /// </summary>
        protected virtual void ReleaseManagedResources()
        {
        }

        /// <summary>
        /// Fires after the dispoing changed event.
        /// </summary>
        protected virtual void FireDisposingChanged()
        {
        }

        /// <summary>
        /// Fires after the dispose changed event.
        /// </summary>
        protected virtual void FireDisposedChanged()
        {
        }

        /// <summary>
        /// Throws an exception if the current object has been disposed.
        /// </summary>
        protected void EnsureNotDisposed()
        {
            if (((IDisposableObject)this).IsDisposed)
                throw new InvalidOperationException(Resources.ExcDispBaseDisposed);
        }

        [DebuggerNonUserCode]
        [DebuggerHidden]
        [DebuggerStepThrough]
        private void HandleDisposeErrors(IList<Exception> disposeErrors)
        {
            if (disposeErrors.Count > 0)
            {
                throw new DisposeException(Resources.ExcDisposeFinishedWithError, disposeErrors);
            }
        }

        private void LogInstanceDisposed()
        {
            if (Logger<DisposeBase>.IsDebugEnabled)
            {
                Logger<DisposeBase>.DebugFormat(Resources.LogDispBaseInstanceDisposed, GetType().FullName);
            }
        }

        private void TryReleaseManagedResources(IList<Exception> disposeErrors)
        {
            try { ReleaseManagedResources(); }
            catch (Exception e)
            {
                CriticalLogger<DisposeBase>.Log(Resources.ExcDisposeManagedResourcesFailed, e);
                disposeErrors.Add(e);
            }
        }

        private void TryReleaseUnmanagedResources(IList<Exception> disposeErrors)
        {
            try { ReleaseUnmanagedResources(); }
            catch (Exception e)
            {
                CriticalLogger<DisposeBase>.Log(Resources.ExcDisposeUnmanagedResourcesFailed, e);
                disposeErrors.Add(e);
            }
        }

        private void TryFireDisposingChanged(IList<Exception> disposeErrors)
        {
            try { FireDisposingChanged(); }
            catch (Exception e)
            {
                CriticalLogger<DisposeBase>.Log(Resources.ExcDisposeDisposingChanged, e);
                disposeErrors.Add(e);
            }
        }

        private void TryFireDisposedChanged(IList<Exception> disposeErrors)
        {
            try { FireDisposedChanged(); }
            catch (Exception e)
            {
                CriticalLogger<DisposeBase>.Log(Resources.ExcDisposeDisposedChanged, e);
                disposeErrors.Add(e);
            }
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}